﻿using AutoUpdaterDotNET;

using MergeExcel.Common;

using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MergeExcel.Forms {

    public partial class FrmMain : Form {

        public FrmMain()
        {
            InitializeComponent();
            InitControlTypes();
        }

        #region 启用/禁用界面

        private List<Type> _controlsResponsable;

        private void InitControlTypes()
        {
            _controlsResponsable = new List<Type>
            {
                typeof(TextBox),
                typeof(Button),
                typeof(ComboBox),
                typeof(RadioButton),
                typeof(DataGridView),
                typeof(CheckBox),
                typeof(TreeView),
                typeof(PictureBox)
            };
        }

        private void EnForm(bool s)
        {
            void ef()
            {
                foreach (Control ctrl in Controls)
                {
                    CheckControls(ctrl, s);
                }
            }
            if (InvokeRequired)
                Invoke(new MethodInvoker(() => ef()));
            else
                ef();
        }

        private void CheckControls(Control ctrl, bool s)
        {
            foreach (Control control in ctrl.Controls)
            {
                CheckControls(control, s);
                var typ = control.GetType();
                if (_controlsResponsable.Contains(typ))
                {
                    if (control.Tag?.ToString() != "disable")
                        control.Enabled = s;
                }
            }
        }

        #endregion 启用/禁用界面

        #region 设置信息提示

        private void SetMsg(string txt)
        {
            if (InvokeRequired)
                Invoke(new MethodInvoker(() => lbMessage.Text = txt));
            else
                lbMessage.Text = txt;
        }

        private void SetTableTips(int allCount, int dataCount, string tableName)
        {
            void st()
            {
                if (tableName == "A")
                    lbTableATip.Text = $"{tableName}表总行数: {allCount} , 数据行数: {dataCount}";
                else if (tableName == "B")
                    lbTableBTip.Text = $"{tableName}表总行数: {allCount} , 数据行数: {dataCount}";
            }
            if (InvokeRequired)
                Invoke(new MethodInvoker(() => st()));
            else
                st();
        }

        #endregion 设置信息提示

        #region 设置进度条

        private void SetProgress(int value, int total)
        {
            void sp()
            {
                pgMain.Maximum = total;
                pgMain.Value = value;
            }
            if (InvokeRequired)
                Invoke(new MethodInvoker(() => sp()));
            else
                sp();
        }

        private void ProgressGo()
        {
            if (InvokeRequired)
                Invoke(new MethodInvoker(() => pgMain.PerformStep()));
            else
                pgMain.PerformStep();
        }

        #endregion 设置进度条

        #region 窗口载入

        private void FrmMain_Shown(object sender, EventArgs e)
        {
            EnForm(false);
            if (Environment.Is64BitProcess)
                lbRunMode.Text = "x64";
            else
                lbRunMode.Text = "x86";
            lbVersion.Text = Assembly.GetExecutingAssembly().GetName().Version.ToString();
            WriteConfigToTextBox();
            SetCommonMethods();
            if (!Comm.UpdateServer.Empty())
            {
                SetMsg("正在检查更新");
                AutoUpdater.CheckForUpdateEvent += Update_Checked;
                AutoUpdater.Start(Comm.UpdateServer + "MergeExcel.xml?" + Guid.NewGuid().ToString());
            }
            else
            {
                EnForm(true);
                AddFormsToTab();
            }
        }

        private void Update_Checked(UpdateInfoEventArgs args)
        {
            if (args is null)
            {
                SetMsg("检查更新失败");
                EnForm(true);
                AddFormsToTab();
                return;
            }
            if (args.Error != null)
            {
                if (!args.Error.Message.Empty())
                {
                    SetMsg("检查更新失败:" + args.Error.Message);
                    EnForm(true);
                    AddFormsToTab();
                    return;
                }
            }

            if (args.IsUpdateAvailable)
            {
                SetMsg("有新版本，准备更新...");

                try
                {
                    if (AutoUpdater.DownloadUpdate(args))
                    {
                        Application.Exit();
                    }
                }
                catch (Exception exception)
                {
                    MessageBox.Show(exception.Message, exception.GetType().ToString(), MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
                    EnForm(true);
                }
            }
            else
            {
                SetMsg("当前版本为最新版本！");
                EnForm(true);
                AddFormsToTab();
            }
        }

        private void SetCommonMethods()
        {
            Comm.SetProgress = SetProgress;
            Comm.SetMsg = SetMsg;
            Comm.EF = EnForm;
            Comm.ProgressGo = ProgressGo;
            Comm.OutputFileName = getOutFileName;
            string getOutFileName()
            {
                string s = "";
                if (InvokeRequired)
                    Invoke(new MethodInvoker(() => s = tbxSaveFile.Text));
                else
                    s = tbxSaveFile.Text;
                return s;
            }
        }

        private void AddFormsToTab()
        {
            AddForm(new FrmDifference(), tbDifference);
            AddForm(new FrmSameness(), tbSame);
            AddForm(new FrmSameMerge(), tbMerge);
        }

        private void WriteConfigToTextBox()
        {
            tbxTableASheetIndex.Text = Comm.Cfg.TableASheetIndex.ToString();
            tbxTableACaptionCount.Text = Comm.Cfg.TableACaptionCount.ToString();
            tbxTableACaptionIndex.Text = Comm.Cfg.TableACaptionIndex.ToString();
            tbxTableBSheetIndex.Text = Comm.Cfg.TableBSheetIndex.ToString();
            tbxTableBCaptionCount.Text = Comm.Cfg.TableBCaptionCount.ToString();
            tbxTableBCaptionIndex.Text = Comm.Cfg.TableBCaptionIndex.ToString();
        }

        #endregion 窗口载入

        #region 增加窗口到TabControl

        private void AddForm(Form f, TabPage tb)
        {
            f.TopLevel = false;
            f.FormBorderStyle = FormBorderStyle.None;
            f.Dock = DockStyle.Fill;
            tb.Controls.Add(f);
            f.Show();
        }

        #endregion 增加窗口到TabControl

        #region 显示样例界面

        private void BtnHelp_Click(object sender, EventArgs e)
        {
            new FrmHelp().ShowDialog();
        }

        #endregion 显示样例界面

        #region 选择文件A

        private void BtnSelectA_Click(object sender, EventArgs e)
        {
            string fileName = SelectFile();
            if (fileName.Empty())
                return;
            tbxFileA.Text = fileName;
            SetTableTips(0, 0, "A");
            Comm.TableA.Rows?.Clear();
            Comm.TableA.Columns?.Clear();
        }

        #endregion 选择文件A

        #region 选择文件B

        private void BtnSelectB_Click(object sender, EventArgs e)
        {
            string fileName = SelectFile();
            if (fileName.Empty())
                return;
            tbxFileB.Text = fileName;
            SetTableTips(0, 0, "B");
            Comm.TableB.Rows?.Clear();
            Comm.TableB.Columns?.Clear();
        }

        #endregion 选择文件B

        #region 选择文件

        private string SelectFile()
        {
            OpenFileDialog dlg = new OpenFileDialog
            {
                Filter = "Excel文件|*.xls;*.xlsx"
            };
            if (dlg.ShowDialog() != DialogResult.OK)
                return null;
            return dlg.FileName;
        }

        #endregion 选择文件

        #region 保存配置

        private void SaveSetting()
        {
            Comm.Cfg.TableASheetIndex = tbxTableASheetIndex.Text.ToInt32();
            Comm.Cfg.TableACaptionCount = tbxTableACaptionCount.Text.ToInt32();
            Comm.Cfg.TableACaptionIndex = tbxTableACaptionIndex.Text.ToInt32();

            Comm.Cfg.TableBSheetIndex = tbxTableBSheetIndex.Text.ToInt32();
            Comm.Cfg.TableBCaptionCount = tbxTableBCaptionCount.Text.ToInt32();
            Comm.Cfg.TableBCaptionIndex = tbxTableBCaptionIndex.Text.ToInt32();
            Comm.SaveConfig();
        }

        #endregion 保存配置

        #region 读取文件

        private async void BtnReadData_Click(object sender, EventArgs e)
        {
            try
            {
                EnForm(false);
                SaveSetting();
                if (tbxFileA.Text.Empty())
                    throw new Exception("必须选择文件A");
                if (tbxFileB.Text.Empty())
                    throw new Exception("必须选择文件B");
                string fileNameA = tbxFileA.Text.Trim();
                string fileNameB = tbxFileB.Text.Trim();
                Comm.TableA.Rows?.Clear();
                Comm.TableA.Columns?.Clear();
                Comm.TableB.Rows?.Clear();
                Comm.TableB.Columns?.Clear();
                SetTableTips(0, 0, "A");
                SetTableTips(0, 0, "B");
                var err = await Task.Run(() =>
                {
                    try
                    {
                        // 读取文件A
                        ReadExcelFile(
                            Comm.TableA,
                            fileNameA,
                            Comm.Cfg.TableACaptionIndex,
                            Comm.Cfg.TableACaptionCount,
                            Comm.Cfg.TableASheetIndex - 1,
                            "A");
                        // 读取文件B
                        ReadExcelFile(
                            Comm.TableB,
                            fileNameB,
                            Comm.Cfg.TableBCaptionIndex,
                            Comm.Cfg.TableBCaptionCount,
                            Comm.Cfg.TableBSheetIndex - 1,
                            "B");
                        SetProgress(0, 0);
                        SetMsg("读取数据完成");
                        return null;
                    }
                    catch (Exception ex)
                    {
                        return ex.Message;
                    }
                });
                if (!err.Empty())
                    throw new Exception(err);
                EnForm(true);
            }
            catch (Exception ex)
            {
                EnForm(true);
                MessageBox.Show(ex.Message);
            }
        }

        private void ReadExcelFile(DataTable dt, string fileName, int captionIndex, int captionCount, int sheetIndex, string tableName)
        {
            SetMsg($"正在打开文件{tableName}");
            var sheet = LoadExcelSheet(fileName, sheetIndex, tableName);
            int totalCount = sheet.PhysicalNumberOfRows;
            if (totalCount < 1 || totalCount <= captionCount)
                throw new Exception($"表{tableName}中没有数据");
            int dataCount = totalCount - captionCount;
            // 读取标题
            IRow row;
            GetCaption(dt, captionIndex, tableName, sheet);
            // 读取数据
            int cnt = 1;
            SetProgress(0, dataCount);
            for (int i = captionCount; i < totalCount; i++)
            {
                if (i == totalCount - 1)
                    cnt = 10;
                if (cnt >= 10)
                {
                    Thread.Sleep(1);
                    Application.DoEvents();
                    cnt = 1;
                    SetMsg($"正在读取表{tableName}数据: {i - captionCount + 1}/{dataCount}");
                }
                ProgressGo();
                row = sheet.GetRow(i);
                if (row != null)
                {
                    ReadDataRow(dt, row);
                }
                cnt++;
            }
            SetTableTips(totalCount, dataCount, tableName);
        }

        private void ReadDataRow(DataTable dt, IRow row)
        {
            var dtRow = dt.NewRow();
            for (int j = 0; j < dt.Columns.Count; j++)
            {
                var cell = row.GetCell(j);
                if (cell is null)
                    dtRow[j] = "";
                else
                {
                    cell.SetCellType(CellType.String);
                    dtRow[j] = cell.StringCellValue;
                }
            }
            dt.Rows.Add(dtRow);
        }

        private void GetCaption(DataTable dt, int captionIndex, string tableName, ISheet sheet)
        {
            var row = sheet.GetRow(captionIndex - 1);
            if (row is null)
                throw new Exception($"表{tableName}标题字段读取失败");
            for (int i = 0; i < row.LastCellNum; i++)
            {
                var cell = row.GetCell(i);
                cell?.SetCellType(CellType.String);
                string name = cell?.StringCellValue;
                if (cell.IsMergedCell)
                    name = GetMergedCellValue(cell);
                if (name.Empty())
                    throw new Exception($"表{tableName}第{i + 1}列:读取列名失败");
                if (!dt.Columns.Contains(name))
                    dt.Columns.Add(name, typeof(string));
            }
        }

        private string GetMergedCellValue(ICell cell)
        {
            if (cell is null)
                return "";
            for (int i = 0; i < cell.Sheet.NumMergedRegions; i++)
            {
                var cellRange = cell.Sheet.GetMergedRegion(i);
                if (cell.ColumnIndex >= cellRange.FirstColumn && cell.ColumnIndex <= cellRange.LastColumn && cell.RowIndex >= cellRange.FirstRow && cell.RowIndex <= cellRange.LastRow)
                {
                    var cl = cell.Sheet.GetRow(cellRange.FirstRow).GetCell(cellRange.FirstColumn);
                    cl?.SetCellType(CellType.String);
                    return cl?.StringCellValue;
                }
            }
            return "";
        }

        private ISheet LoadExcelSheet(string fileName, int sheetIndex, string tableName)
        {
            IWorkbook book;
            if (fileName.ToLower().EndsWith(".xlsx"))
                book = new XSSFWorkbook(fileName);
            else if (fileName.ToLower().EndsWith("*.xls"))
            {
                FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                book = new HSSFWorkbook(fs);
                fs.Close();
                fs.Dispose();
            }
            else
                throw new Exception($"表{tableName}文件格式不正确");
            if (book.NumberOfSheets < sheetIndex + 1)
                throw new Exception($"表{tableName}参数[表页序号]设置不正确");
            var sheet = book.GetSheetAt(sheetIndex);
            if (sheet is null)
                throw new Exception($"表{tableName}中未找到指定表页");
            return sheet;
        }

        #endregion 读取文件

        #region 选择输出文件

        private void BtnSelectSaveFile_Click(object sender, EventArgs e)
        {
            SaveFileDialog sf = new SaveFileDialog
            {
                Filter = "Excel文件|*.xlsx;*.xls",
                FileName = $"结果_{DateTime.Now.Format().Date}.xlsx"
            };
            if (sf.ShowDialog() != DialogResult.OK)
                return;
            tbxSaveFile.Text = sf.FileName;
        }

        #endregion 选择输出文件
    }
}